
/***************************************************************************
 *   sakcab 
 *                                                                         *
 *   Copyright (C) 2010 by kuerant                                         *
 *                         <kuerant@gmail.com>                             *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This library is distributed in the hope that it will be usefull, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
 *   Library General Public License for more details.                      *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston,                *
 *   MA 02111-1307, USA                                                    *
 ***************************************************************************/


#include <unistd.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>

#include <boost/program_options.hpp>
namespace po = boost::program_options;

#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::posix_time;

#include <pthread.h>
#include <signal.h>

#include "bocat/common/base64.hpp"
#include "bocat/common/logging.hpp"
#include "bocat/common/time.hpp"
#include "bocat/common/url.hpp"
#include "bocat/common/tinyxml.hpp"

#include "sakcab/client.hpp"


static sakcab::client::Client*	sg_client_p = NULL;

bocat::common::Logger*    logger_p = NULL;

#define INFO(ARGS...) 	logger_p->info(__LINE__, __FILE__, ARGS)
#define ERROR(ARGS...) 	logger_p->error(__LINE__, __FILE__, ARGS)


static	long
getTimeDuration( ptime time_begin, ptime time_end )
{
    time_period tp( time_begin, time_end );
    time_duration   td = tp.length();
    long    time_last_ms = td.total_milliseconds();
    //float   time_last_s = time_last_ms / 1000.0f;

    return time_last_ms;
}


typedef	std::map< std::string, std::string >	result_type;
static	int	query( const std::string& url, result_type& result, bool verbose );

int main(int argc, char* argv[])
{
    std::string host;
    std::string port;

	std::string	url;

	bool	verbose = false;

    try {
        po::options_description desc("Allowed options");
        desc.add_options()
            ("help,h", "produce help message")
            ("verbose,v", "show verbose message")

            ("host", po::value<std::string>(&host)->default_value("127.0.0.1"), "server ip/hostname")
            ("port", po::value<std::string>(&port)->default_value("5137"), "server port")

            ("url", po::value<std::string>(&url), "url to find")
        ;

        po::variables_map vm;
        po::store(po::parse_command_line(argc, argv, desc), vm);
        po::notify(vm);

        if (vm.count("help")) {
            std::cout << desc << "\n";
            return 1;
        }
		
		if (vm.count("verbose")) {
			verbose = true;
		}

		if ( vm.count("url") < 1) {
			std::cout << "\nNo url to query\n\n";
            std::cout << desc << "\n";
            return 1;
		}

    }
    catch(std::exception& e) {
        std::cerr << "error: " << e.what() << "\n";
        return 1;
    }
    catch(...) {
        std::cerr << "Exception of unknown type!\n";
        return 2;
    }

	logger_p = bocat::common::getLogger();

	// prepare client
	int	port_i = boost::lexical_cast< int >( port.c_str() );
	sg_client_p	= new sakcab::client::Client( host.c_str(), port_i );

	result_type	result;
	try {
		boost::trim( url );
		int	rc = query( url, result, verbose );
		if ( rc < 0 ) {
			// ERROR
			return	254;
		}
	}
	catch( std::exception& e ) {
		std::cerr << "ERROR : " << e.what() << std::endl;
		return	255;
	}
	
	delete sg_client_p;
	sg_client_p = NULL;

	// show result
	result_type::iterator	end = result.end();
	result_type::iterator	it = result.begin();
	for ( ; end != it; ++it ) {
		const std::string&	key = it->first;
		const std::string&	value = it->second;

		std::cout << key << " : " << value << std::endl;
	}

	return 0;
}


///////////////////////////////////////////////////////////////////////


static	const char* getValueByPath( TiXmlHandle root_handle, const char* path[] );

static	void	extract( const std::string& xml_data, result_type& result );

static	int	query( const std::string& url, result_type& result, bool verbose )
{
	if ( url.size() < 7 ) {
		return	-1;
	}

	int		rc = 0;

	ptime   time_start = microsec_clock::local_time();

	std::string	url_reversed = url;
	if ( 0 == strncmp(url.c_str(), "http", 4) ) {
		bocat::common::URL	url_c( url );
		url_reversed = url_c.getReversedURL();
	}

	std::string	data;

	if ( verbose ) {
		INFO( "  query url '%s' ...", url_reversed.c_str() );
	}
	rc = sg_client_p->find( url_reversed, data );
	if ( rc < 0 ) {
		return	-2;
	}

	extract( data, result );
	result.insert( std::make_pair( "data", data ) );

	if ( verbose ) {
		ptime   time_stop = microsec_clock::local_time();
		long    time_last_ms = getTimeDuration( time_start, time_stop );
		INFO( "  time last %ld milli-seconds", time_last_ms );
	}

	return	0;
}


static	std::string	NA( "N/A" );


void
extract( const std::string& xml_data, result_type& result )
{
	/*
	'pkgid' : 'document/key/pkgid',
	'cid' : 'document/wbng_storage_se/cid',
	'download_time' : 'document/vspider/metadata/downloadtime',
	'title' : 'document/vspider/bbs-index/title',
	'junk' : 'document/junk-filter/bbs_junk_type',
	'people_rank' : 'document/peoplerank/pr_rank',
	'dup_title_id' : 'document/duplicate/title/repeat_id',
	'dup_content_id' : 'document/duplicate/content/repeat_id',
	*/

	TiXmlDocument   doc;

	try {
		doc.Parse( xml_data.c_str() );
	}
	catch( ... ) {
		return;
	}

	TiXmlHandle xml( &doc );
	TiXmlHandle document_handle = xml.FirstChildElement("document");

	// 'pkgid' : 'document/key/pkgid',
	result.insert( std::make_pair( "pkgid", NA ) );

	// 'cid' : 'document/wbng_storage_se/cid',
    const char* cid_path[] = {"wbng_storage_se","cid",NULL};
    const char* cid = getValueByPath( document_handle, cid_path );
    if ( NULL == cid ) {
        result.insert( std::make_pair( "cid", NA ) );
    }
    else {
        result.insert( std::make_pair( "cid", cid ) );
    }
	
    // 'download_time' : 'document/vspider/metadata/downloadtime',
    const char* dltime_path[] = {"vspider","metadata","downloadtime",NULL};
    const char* download_time = getValueByPath( document_handle, dltime_path );
    if ( NULL == download_time ) {
        result.insert( std::make_pair( "download_time", NA ) );
    }
    else {
        result.insert( std::make_pair( "download_time", download_time ) );
    }

    // 'title' : 'document/vspider/bbs-index/title',
    const char* title_path[] = {"vspider","bbs-index","title",NULL};
    const char* title = getValueByPath( document_handle, title_path );
    if ( NULL == title ) {
        const char* title_path[] = {"vspider","blog-index","title",NULL};
        title = getValueByPath( document_handle, title_path );
    }
    if ( NULL == title ) {
        result.insert( std::make_pair( "title", NA ) );
    }
    else {
        result.insert( std::make_pair( "title", title ) );
    }

    // 'junk' : 'document/junk-filter/bbs_junk_type',
    const char* junk_path[] = {"junk-filter","bbs_junk_type",NULL};
    const char* junk = getValueByPath( document_handle, junk_path );
    if ( NULL == junk ) {
        const char* junk_path[] = {"junk-filter","blog_junk_type",NULL};
        junk = getValueByPath( document_handle, junk_path );
    }
    if ( NULL == junk ) {
        result.insert( std::make_pair( "junk", NA ) );
    }
    else {
        result.insert( std::make_pair( "junk", junk ) );
    }

    // 'people_rank' : 'document/peoplerank/pr_rank',
    const char* people_rank_path[] = {"peoplerank","pr_rank",NULL};
    const char* people_rank = getValueByPath( document_handle, people_rank_path );
    if (NULL == people_rank) {
        result.insert( std::make_pair( "people_rank", NA ) );
    }
    else {
        result.insert( std::make_pair( "people_rank", people_rank ) );
    }

    // 'dup_title_id' : 'document/duplicate/title/repeat_id',
    const char* dup_title_id_path[] = {"duplicate","title","repeat_id", NULL};
    const char* dup_title_id = getValueByPath( document_handle, dup_title_id_path );
    if ( NULL == dup_title_id ) {
        result.insert( std::make_pair( "dup_title_id", NA ) );
    }
    else {
        result.insert( std::make_pair( "dup_title_id", dup_title_id ) );
    }

    // 'dup_content_id' : 'document/duplicate/content/repeat_id',
    const char* dup_content_id_path[] = {"duplicate","content","repeat_id", NULL};
    const char* dup_content_id = getValueByPath( document_handle, dup_content_id_path );
    if ( NULL == dup_content_id ) {
        result.insert( std::make_pair( "dup_content_id", NA ) );
    }
    else {
        result.insert( std::make_pair( "dup_content_id", dup_content_id ) );
    }

}

const char*
getValueByPath( TiXmlHandle root_handle, const char* path[] )
{
    TiXmlHandle handle = root_handle;

    for ( int i=0; NULL != path[i]; ++i ) { 
        handle = handle.FirstChildElement( path[i] );
    }   

    TiXmlNode*  node_p = handle.Node();
    if ( NULL == node_p ) { 
        return  NULL;
    }   
    
    TiXmlNode*  text_np = node_p->FirstChild();
    if ( NULL == text_np ) { 
        return  NULL;
    }   

    return  text_np->Value();
}



